package cn.edu.scau.cmi.ema.service;

import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import cn.edu.scau.cmi.ema.dao.*;
import cn.edu.scau.cmi.ema.domain.*;
import cn.edu.scau.cmi.ema.util.CmiSqlUtil;
import cn.edu.scau.cmi.ema.domain.base.CmiPagination;
import cn.edu.scau.cmi.ema.service.base.CmiEmaService;

import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import javax.persistence.Query;
import java.util.Iterator;
@Service("StandardService")
@Transactional

public class StandardServiceImpl extends CmiEmaService implements StandardService {
	public StandardServiceImpl() {
	}

	@Transactional
	public Standard saveStandard(Standard standard) {
		Set<Featurestandard> existingFeaturestandardsForStandard = new HashSet<Featurestandard>();
		Set<Foodtype> existingFoodtypesForFoodtype = new HashSet<Foodtype>();
		Set<Standard> existingStandardsForReplacedstandard = new HashSet<Standard>();

		Set<Featurestandard> selectedFeaturestandardsForStandard = standard.getFeaturestandardsForStandard();
		Set<Foodtype> selectedFoodtypesForFoodtype = standard.getFoodtypesForStandard();
		Set<Standard> selectedStandardsForReplacedstandard = standard.getStandardsForReplacedstandard();

		if (standard.getId() == null) {
			Standard replacedstandard = standardDAO.findStandardById(standard.getReplacedstandard().getId());
			Standardtype standardtype = standardtypeDAO.findStandardtypeById(standard.getStandardtype().getId());
			Standardlevel standardlevel = standardlevelDAO.findStandardlevelById(standard.getStandardlevel().getId());

			Standard existingStandard = standardDAO.findStandardByPhysicalPrimaryKey(standard.getNumber());

			standard.setReplacedstandard(replacedstandard);
			standard.setStandardtype(standardtype);
			standard.setStandardlevel(standardlevel);

			if (isEquals(standard, existingStandard)) {
				return existingStandard;
			}

			standard = standardDAO.store(standard);
			standardDAO.flush();

			for (Featurestandard relatedFeaturestandard : selectedFeaturestandardsForStandard) {
				relatedFeaturestandard.setStandard(standard);
				featurestandardDAO.store(relatedFeaturestandard);
			}

			for (Foodtype relatedFoodtype : selectedFoodtypesForFoodtype) {
				relatedFoodtype.getStandardsForFoodtype().add(standard);
				foodtypeDAO.store(relatedFoodtype);
			}
			for (Standard relatedStandard : selectedStandardsForReplacedstandard) {
				relatedStandard.setReplacedstandard(standard);
				standardDAO.store(relatedStandard);
			}
		} else {
			Standard existingStandard = standardDAO.findStandardByPrimaryKey(standard.getId());

			existingFeaturestandardsForStandard = existingStandard.getFeaturestandardsForStandard();
			existingFoodtypesForFoodtype = existingStandard.getFoodtypesForStandard();
			existingStandardsForReplacedstandard = existingStandard.getStandardsForReplacedstandard();

			Set<Featurestandard> prepareDeleteFeaturestandardsForStandard = new HashSet<Featurestandard>();
			Set<Featurestandard> prepareAddFeaturestandardsForStandard = new HashSet<Featurestandard>();

			prepareDeleteFeaturestandardsForStandard.addAll(selectedFeaturestandardsForStandard);
			prepareDeleteFeaturestandardsForStandard.addAll(existingFeaturestandardsForStandard);

			prepareAddFeaturestandardsForStandard.addAll(selectedFeaturestandardsForStandard);
			prepareAddFeaturestandardsForStandard.addAll(existingFeaturestandardsForStandard);

			prepareDeleteFeaturestandardsForStandard.removeAll(selectedFeaturestandardsForStandard);
			prepareAddFeaturestandardsForStandard.removeAll(existingFeaturestandardsForStandard);

			for (Featurestandard relatedFeaturestandard : prepareAddFeaturestandardsForStandard) {
				relatedFeaturestandard.setStandard(standard);
				featurestandardDAO.store(relatedFeaturestandard);
			}

			for (Featurestandard relatedFeaturestandard : prepareDeleteFeaturestandardsForStandard) {
				relatedFeaturestandard.setStandard(null);
				featurestandardDAO.store(relatedFeaturestandard);
			}
			Set<Foodtype> prepareDeleteFoodtypesForFoodtype = new HashSet<Foodtype>();
			Set<Foodtype> prepareAddFoodtypesForFoodtype = new HashSet<Foodtype>();

			prepareDeleteFoodtypesForFoodtype.addAll(selectedFoodtypesForFoodtype);
			prepareDeleteFoodtypesForFoodtype.addAll(existingFoodtypesForFoodtype);

			prepareAddFoodtypesForFoodtype.addAll(selectedFoodtypesForFoodtype);
			prepareAddFoodtypesForFoodtype.addAll(existingFoodtypesForFoodtype);

			prepareDeleteFoodtypesForFoodtype.removeAll(selectedFoodtypesForFoodtype);
			prepareAddFoodtypesForFoodtype.removeAll(existingFoodtypesForFoodtype);

			for (Foodtype relatedFoodtype : prepareAddFoodtypesForFoodtype) {

				relatedFoodtype.getStandardsForFoodtype().add(existingStandard);
				foodtypeDAO.store(relatedFoodtype);
			}

			for (Foodtype relatedFoodtype : prepareDeleteFoodtypesForFoodtype) {

				relatedFoodtype.getStandardsForFoodtype().remove(existingStandard);
				foodtypeDAO.store(relatedFoodtype);
			}
			Set<Standard> prepareDeleteStandardsForReplacedstandard = new HashSet<Standard>();
			Set<Standard> prepareAddStandardsForReplacedstandard = new HashSet<Standard>();

			prepareDeleteStandardsForReplacedstandard.addAll(selectedStandardsForReplacedstandard);
			prepareDeleteStandardsForReplacedstandard.addAll(existingStandardsForReplacedstandard);

			prepareAddStandardsForReplacedstandard.addAll(selectedStandardsForReplacedstandard);
			prepareAddStandardsForReplacedstandard.addAll(existingStandardsForReplacedstandard);

			prepareDeleteStandardsForReplacedstandard.removeAll(selectedStandardsForReplacedstandard);
			prepareAddStandardsForReplacedstandard.removeAll(existingStandardsForReplacedstandard);

			for (Standard relatedStandard : prepareAddStandardsForReplacedstandard) {
				relatedStandard.setReplacedstandard(standard);
				standardDAO.store(relatedStandard);
			}

			for (Standard relatedStandard : prepareDeleteStandardsForReplacedstandard) {
				relatedStandard.setReplacedstandard(null);
				standardDAO.store(relatedStandard);
			}
			standard = standardDAO.store(standard);
			standardDAO.flush();
		}

		featurestandardDAO.flush();
		foodtypeDAO.flush();
		standardDAO.flush();
		return standard;
	}

	public void deleteStandard(Standard standard) {
		standardDAO.remove(standard);
		standardDAO.flush();
	}

	@Transactional
	public void deleteStandards(Set<Integer> deleteStandardsId) {
		Query query = standardDAO.createNamedQuery("deleteStandardsById", -1, -1);
		query.setParameter(1, deleteStandardsId);
		query.executeUpdate();
	}

	@Transactional
	public Set<Standard> loadStandards() {
		return standardDAO.findAllStandards();

	}

	@Transactional
	public Set<Standard> loadReferenceStandards(Set<Standard> standards) {
		Iterator<Standard> standardsIterator = standards.iterator();
		Set<Standard> referencedStandards = new HashSet<>();
		while (standardsIterator.hasNext()) {
			Standard currentStandard = standardsIterator.next();

			if (!currentStandard.getFeaturestandardsForStandard().isEmpty()) {
				referencedStandards.add(currentStandard);
				continue;
			}

			if (!currentStandard.getFoodtypesForStandard().isEmpty()) {
				referencedStandards.add(currentStandard);
				continue;
			}

			if (!currentStandard.getStandardsForReplacedstandard().isEmpty()) {
				referencedStandards.add(currentStandard);
				continue;
			}
		}
		return referencedStandards;
	}

	public Set<Standard> loadStandardsByIdSet(Set<Integer> standardIdSet) {

		Iterator<Integer> standardIdIterator = standardIdSet.iterator();
		Set<Standard> standardSet = new HashSet<>();
		while (standardIdIterator.hasNext()) {
			Integer id = standardIdIterator.next();
			Standard currentStandard = standardDAO.findStandardById(id);
			standardSet.add(currentStandard);
		}
		return standardSet;
	}

	@Transactional
	public List<Standard> findAllStandards(Integer startResult, Integer maxRows) {
		return new java.util.ArrayList<Standard>(standardDAO.findAllStandards(startResult, maxRows));
	}

	@Transactional
	public Standard findStandardByPrimaryKey(Integer id) {
		return standardDAO.findStandardByPrimaryKey(id);
	}

	@Transactional
	public Integer countStandards() {
		return ((Long) standardDAO.createQuerySingleResult("select count(o) from Standard o").getSingleResult())
				.intValue();
	}

	@Transactional
	public Integer countStandards(CmiPagination page) {
		String sql = CmiSqlUtil.createCountHql(page, Standard.class);
		return ((Long) standardDAO.createQuerySingleResult(sql).getSingleResult()).intValue();
	}

	/**
	 * search for name with ajax
	 * 
	 */
	@Transactional
	public Integer countStandardsByName(String queryName) {
		Query query = standardDAO.createNamedQuery("countStandardsByName", -1, -1);
		query.setParameter(1, queryName);
		return new Long((long) query.getSingleResult()).intValue();
	}

	@Transactional
	public Integer countStandardsByDetachedCriteria(CmiPagination page) {
		DetachedCriteria detachedCriteria = CmiSqlUtil.createDetachedCriteria(page, Standard.class);
		Session session = entityManager.unwrap(Session.class);
		detachedCriteria.setProjection(Projections.rowCount());
		return new Long((long) detachedCriteria.getExecutableCriteria(session).uniqueResult()).intValue();
	}

	public Map<String, ?> loadStandardObjectReferencedSet(Standard standard) {
		HashMap<String, Object> objectReferencedSetMap = new HashMap<>();
		if (standard.getReplacedstandard() != null) {

			Set<Integer> replacedstandardIds = new HashSet<Integer>();
			replacedstandardIds.add(standard.getReplacedstandard().getId());
			objectReferencedSetMap.put("replacedstandardIds", replacedstandardIds);
		}
		if (standard.getStandardtype() != null) {

			Set<Integer> standardtypeIds = new HashSet<Integer>();
			standardtypeIds.add(standard.getStandardtype().getId());
			objectReferencedSetMap.put("standardtypeIds", standardtypeIds);
		}
		if (standard.getStandardlevel() != null) {

			Set<Integer> standardlevelIds = new HashSet<Integer>();
			standardlevelIds.add(standard.getStandardlevel().getId());
			objectReferencedSetMap.put("standardlevelIds", standardlevelIds);
		}

		Set<Integer> featurestandardsIds = new HashSet<Integer>();
		for (Featurestandard obj : standard.getFeaturestandardsForStandard()) {

			featurestandardsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("featurestandardsIds", featurestandardsIds);
		Set<Integer> foodtypesIds = new HashSet<Integer>();
		for (Foodtype foodtype : standard.getFoodtypesForStandard()) {
			foodtypesIds.add(foodtype.getId());
		}
		objectReferencedSetMap.put("foodtypesIds", foodtypesIds);
		Set<Integer> standardsIds = new HashSet<Integer>();
		for (Standard obj : standard.getStandardsForReplacedstandard()) {

			standardsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("standardsIds", standardsIds);

		return objectReferencedSetMap;
	}

	public Map<String, ?> loadStandardClassReferencedSet(Standard standard) {
		HashMap<String, Object> classReferencedSetMap = new HashMap<>();
		classReferencedSetMap.put("standardtypes", standardtypeDAO.findAllStandardtypes());
		classReferencedSetMap.put("standardlevels", standardlevelDAO.findAllStandardlevels());
		classReferencedSetMap.put("featurestandards", featurestandardDAO.findAllFeaturestandards());
		classReferencedSetMap.put("foodtypes", foodtypeDAO.findAllFoodtypes());
		classReferencedSetMap.put("standards", standardDAO.findAllStandards());

		return classReferencedSetMap;
	}

	public boolean isEquals(Standard standard, Object obj) {
		if (obj == standard)
			return true;
		if (!(obj instanceof Standard))
			return false;
		Standard comparedStandard = (Standard) obj;
		if (!CheckPropertyEqual(standard.getNumber(), comparedStandard.getNumber())) {
			return false;
		}
		return true;
	}

	public static <T> boolean CheckPropertyEqual(T originProperty, T comparedProperty) {
		// 基本类型经过参数传递后会自动装箱
		String[] className = originProperty.getClass().getName().split("\\.");
		if (className[0].equals("java") && className[1].equals("math")) {
			return ((Comparable) originProperty).compareTo(comparedProperty) == 0 ? true : false;
		} else {
			return originProperty.equals(comparedProperty);
		}
	}
}

